#include "chessdetector.h"
#include <QTimer>
#include <QDebug>
#include <QImage>
#include <opencv2/imgproc.hpp>
#include <QDateTime>

ChessDetector::ChessDetector(QObject *parent) : QObject(parent) {
    // 尝试打开摄像头
    for (int i = 21; i < 24; i++) {
        cap.open(i, cv::CAP_V4L2);
        if (cap.isOpened()) {
            qDebug() << "成功打开摄像头 #" << i;
            break;
        }
        
        cap.open(i, cv::CAP_ANY);
        if (cap.isOpened()) {
            qDebug() << "使用默认后端打开摄像头 #" << i;
            break;
        }
    }
    
    if (!cap.isOpened()) {
        qDebug() << "无法打开任何摄像头";
    } else {
        cap.set(cv::CAP_PROP_FRAME_WIDTH, 640);
        cap.set(cv::CAP_PROP_FRAME_HEIGHT, 480);
    }
    
    timer = new QTimer(this);
    connect(timer, &QTimer::timeout, this, &ChessDetector::processVideo);
    
    m_verificationTimer = new QTimer(this);
    m_verificationTimer->setInterval(500);
    connect(m_verificationTimer, &QTimer::timeout, this, [this]() {
        if (m_verificationPending) {
            processVideo();
        }
    });
}

ChessDetector::~ChessDetector() {
    if (cap.isOpened()) cap.release();
    delete timer;
}
cv::Point ChessDetector::getPixelPositionForBoardCoord(int boardX, int boardY) {
    for (const auto& piece : boardPieces) {
        if (piece.boardCoord.x == boardX && piece.boardCoord.y == boardY) {
            return piece.position;
        }
    }
    return cv::Point(-1, -1); // 未找到
}

bool ChessDetector::grabAndProcessFrame() {
    if (!cap.isOpened()) return false;
    
    cv::Mat frame;
    cap >> frame;
    if (frame.empty()) return false;
    
    processFrame(frame);
    return true;
}

cv::Point ChessDetector::convertToMotorCoords(int pixel_x, int pixel_y) {
    int motor_x = pixel_x*53.33 - 7906;
    int motor_y = pixel_y*53.33 + 2681;
    return cv::Point(motor_x, motor_y);
}

std::tuple<std::vector<cv::Point2f>, std::vector<cv::Point>, cv::Mat, int, int> 
ChessDetector::detectChessboard(cv::Mat image) {
    cv::Mat gray, blurred, thresh;
    cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY);
    cv::GaussianBlur(gray, blurred, cv::Size(5, 5), 0);
    cv::adaptiveThreshold(blurred, thresh, 255, cv::ADAPTIVE_THRESH_GAUSSIAN_C, 
                         cv::THRESH_BINARY_INV, 11, 2);
    
    std::vector<std::vector<cv::Point>> contours;
    std::vector<cv::Vec4i> hierarchy;
    cv::findContours(thresh, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE);
    
    std::vector<cv::Point> chessboardContour;
    double maxArea = 0;
    
    for (const auto& cnt : contours) {
        double area = cv::contourArea(cnt);
        if (area > 10000 && area < 20000) {
            double peri = cv::arcLength(cnt, true);
            std::vector<cv::Point> approx;
            cv::approxPolyDP(cnt, approx, 0.02 * peri, true);
            
            if (approx.size() == 4) {
                cv::Rect rect = cv::boundingRect(approx);
                double aspect_ratio = static_cast<double>(rect.width) / rect.height;
                if (aspect_ratio > 0.8 && aspect_ratio < 1.2) {
                    if (area > maxArea) {
                        maxArea = area;
                        chessboardContour = approx;
                    }
                }
            }
        }
    }
    
    if (chessboardContour.empty()) {
        return std::make_tuple(std::vector<cv::Point2f>(), std::vector<cv::Point>(), 
                              cv::Mat(), 0, 0);
    }
    
    std::vector<cv::Point2f> sortedPts(4);
    std::vector<cv::Point> chessContour = chessboardContour;
    
    std::vector<int> indices(4);
    for (int i = 0; i < 4; i++) indices[i] = i;
    
    std::sort(indices.begin(), indices.end(), [&](int a, int b) {
        return chessContour[a].x < chessContour[b].x;
    });
    
    std::vector<cv::Point> left = {chessContour[indices[0]], chessContour[indices[1]]};
    std::vector<cv::Point> right = {chessContour[indices[2]], chessContour[indices[3]]};
    
    std::sort(left.begin(), left.end(), [](cv::Point a, cv::Point b) {
        return a.y < b.y;
    });
    
    std::sort(right.begin(), right.end(), [](cv::Point a, cv::Point b) {
        return a.y < b.y;
    });
    
    sortedPts[0] = left[0];
    sortedPts[1] = right[0];
    sortedPts[2] = right[1];
    sortedPts[3] = left[1];
    
    float width = static_cast<float>(cv::norm(sortedPts[0] - sortedPts[1]));
    float height = static_cast<float>(cv::norm(sortedPts[0] - sortedPts[3]));
    
    std::vector<cv::Point2f> dst = {
        cv::Point2f(0, 0),
        cv::Point2f(width - 1, 0),
        cv::Point2f(width - 1, height - 1),
        cv::Point2f(0, height - 1)
    };
    
    cv::Mat M = cv::getPerspectiveTransform(sortedPts, dst);
    return std::make_tuple(sortedPts, chessContour, M, width, height);
}

int ChessDetector::detectPiece(cv::Mat cell_roi) {
    if (cell_roi.empty()) return 0;
    
    cv::Mat gray, blurred;
    cv::cvtColor(cell_roi, gray, cv::COLOR_BGR2GRAY);
    cv::GaussianBlur(gray, blurred, cv::Size(5, 5), 0);
    
    cv::Mat thresh_black;
    cv::threshold(blurred, thresh_black, 80, 255, cv::THRESH_BINARY_INV);
    
    std::vector<std::vector<cv::Point>> contours_black;
    cv::findContours(thresh_black, contours_black, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
    
    if (!contours_black.empty()) {
        auto max_cnt = *std::max_element(contours_black.begin(), contours_black.end(),
            [](const std::vector<cv::Point>& a, const std::vector<cv::Point>& b) {
                return cv::contourArea(a) < cv::contourArea(b);
            });
        
        double area = cv::contourArea(max_cnt);
        if (area > 50) {
            double peri = cv::arcLength(max_cnt, true);
            double circularity = 4 * CV_PI * area / (peri * peri);
            if (circularity > 0.6) {
                cv::Mat mask = cv::Mat::zeros(gray.size(), CV_8UC1);
                cv::drawContours(mask, std::vector<std::vector<cv::Point>>{max_cnt}, -1, 255, -1);
                cv::Scalar mean_val = cv::mean(cell_roi, mask);
                if (mean_val[0] < 150) {
                    return 2;
                }
            }
        }
    }
    
    cv::Mat thresh_white;
    cv::threshold(blurred, thresh_white, 200, 255, cv::THRESH_BINARY);
    
    std::vector<std::vector<cv::Point>> contours_white;
    cv::findContours(thresh_white, contours_white, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
    
    if (!contours_white.empty()) {
        auto max_cnt = *std::max_element(contours_white.begin(), contours_white.end(),
            [](const std::vector<cv::Point>& a, const std::vector<cv::Point>& b) {
                return cv::contourArea(a) < cv::contourArea(b);
            });
        
        double area = cv::contourArea(max_cnt);
        if (area > 50) {
            double peri = cv::arcLength(max_cnt, true);
            double circularity = 4 * CV_PI * area / (peri * peri);
            if (circularity > 0.7) {
                cv::Mat mask = cv::Mat::zeros(gray.size(), CV_8UC1);
                cv::drawContours(mask, std::vector<std::vector<cv::Point>>{max_cnt}, -1, 255, -1);
                cv::Scalar mean_val = cv::mean(cell_roi, mask);
                if (mean_val[0] > 150) {
                    return 1;
                }
            }
        }
    }
    
    return 0;
}

std::vector<PieceInfo> ChessDetector::detectOutsidePieces(cv::Mat image) {
    cv::Mat gray, blurred;
    cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY);
    cv::GaussianBlur(gray, blurred, cv::Size(5, 5), 0);
    
    std::vector<PieceInfo> pieces;
    
    cv::Mat thresh_black;
    cv::threshold(blurred, thresh_black, 50, 255, cv::THRESH_BINARY_INV);
    
    std::vector<std::vector<cv::Point>> contours_black;
    cv::findContours(thresh_black, contours_black, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
    
    for (const auto& cnt : contours_black) {
        double area = cv::contourArea(cnt);
        if (area > 50) {
            double peri = cv::arcLength(cnt, true);
            double circularity = 4 * CV_PI * area / (peri * peri);
            if (circularity > 0.6) {
                cv::Point2f center;
                float radius;
                cv::minEnclosingCircle(cnt, center, radius);
                pieces.push_back({cv::Point(center), cv::Point(), "", "black", 0, false});
            }
        }
    }
    
    cv::Mat thresh_white;
    cv::threshold(blurred, thresh_white, 200, 255, cv::THRESH_BINARY);
    
    std::vector<std::vector<cv::Point>> contours_white;
    cv::findContours(thresh_white, contours_white, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
    
    for (const auto& cnt : contours_white) {
        double area = cv::contourArea(cnt);
        if (area > 50) {
            double peri = cv::arcLength(cnt, true);
            double circularity = 4 * CV_PI * area / (peri * peri);
            if (circularity > 0.6) {
                cv::Point2f center;
                float radius;
                cv::minEnclosingCircle(cnt, center, radius);
                pieces.push_back({cv::Point(center), cv::Point(), "", "white", 0, false});
            }
        }
    }
    
    std::sort(pieces.begin(), pieces.end(), [](const PieceInfo& a, const PieceInfo& b) {
        return a.position.y < b.position.y;
    });
    
    return pieces;
}

void ChessDetector::processFrame(cv::Mat frame) {
    cv::Mat displayFrame = frame.clone();
    
    auto result = detectChessboard(frame);
    auto sortedPts = std::get<0>(result);
    auto chessContour = std::get<1>(result);
    auto M = std::get<2>(result);
    int width = std::get<3>(result);
    int height = std::get<4>(result);
    
    if (!sortedPts.empty()) {
        std::vector<std::vector<cv::Point>> contours = {chessContour};
        cv::drawContours(displayFrame, contours, -1, cv::Scalar(0, 255, 0), 1);
        
        int cell_w = width / 3;
        int cell_h = height / 3;
        std::vector<cv::Point2f> centers;
        
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                centers.push_back(cv::Point2f((j + 0.5f) * cell_w, (i + 0.5f) * cell_h));
            }
        }
        
        cv::Mat inv_M = cv::getPerspectiveTransform(
            std::vector<cv::Point2f>{
                cv::Point2f(0, 0),
                cv::Point2f(width-1, 0),
                cv::Point2f(width-1, height-1),
                cv::Point2f(0, height-1)
            }, 
            sortedPts
        );
        
        boardPieces.clear();
        std::vector<cv::Point> originalCenters;
        
        for (const auto& center : centers) {
            std::vector<cv::Point2f> src = {center};
            std::vector<cv::Point2f> dst;
            cv::perspectiveTransform(src, dst, inv_M);
            cv::Point pt(dst[0].x, dst[0].y);
            originalCenters.push_back(pt);
            cv::circle(displayFrame, pt, 3, cv::Scalar(0, 0, 255), -1);
        }
        
        if (refCenter.x == 0 && refCenter.y == 0 && originalCenters.size() >= 5) {
            refCenter = originalCenters[4];
            emit detectionInfo(QString("参考点设置: (%1, %2)").arg(refCenter.x).arg(refCenter.y));
        }
        
        cv::Mat warped;
        cv::warpPerspective(frame, warped, M, cv::Size(width, height));
        std::vector<int> states;
        
        const std::vector<int> indexMap = {0, 3, 6, 1, 4, 7, 2, 5, 8};
        
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                cv::Rect roi(j * cell_w, i * cell_h, cell_w, cell_h);
                cv::Mat cell_roi = warped(roi);
                int state = detectPiece(cell_roi);
                
                int origIndex = i * 3 + j;
                int mappedIndex = indexMap[origIndex];
                states.push_back(state);
                
                int logicX = mappedIndex % 3;
                int logicY = mappedIndex / 3;
                
                if (mappedIndex < originalCenters.size()) {
                    cv::putText(displayFrame, QString("(%1,%2):%3")
                                .arg(logicX).arg(logicY).arg(state).toStdString(), 
                               originalCenters[mappedIndex] - cv::Point(30, 0),
                               cv::FONT_HERSHEY_SIMPLEX, 0.7, cv::Scalar(255, 0, 255), 1);
                }
            }
        }
        
        for (int i = 0; i < 9; i++) {
            if (i < originalCenters.size() && i < states.size()) {
                int logicX = i % 3;
                int logicY = i / 3;
                
                std::string stateStr = "空";
                if (states[i] == 1) stateStr = "白";
                else if (states[i] == 2) stateStr = "黑";
                
                PieceInfo piece;
                piece.position = originalCenters[i];
                piece.boardCoord = cv::Point(logicX, logicY);
                piece.color = stateStr;
                piece.name = QString("(%1,%2)").arg(logicX).arg(logicY).toStdString();
                boardPieces.push_back(piece);
            }
        }
    }
    
    auto outsidePieces = detectOutsidePieces(frame);
    
    for (auto& piece : outsidePieces) {
        std::string prefix = (piece.color == "white") ? "W" : "B";
        std::string name = "";
        
        for (int i = 1; i <= 10; i++) {
            std::string candidate = prefix + std::to_string(i);
            auto& targetMap = (piece.color == "white") ? finalWhitePieces : finalBlackPieces;
            if (targetMap.find(candidate) == targetMap.end()) {
                name = candidate;
                break;
            }
        }
        
        if (name.empty()) continue;
        
        auto& targetMap = (piece.color == "white") ? finalWhitePieces : finalBlackPieces;
        
        if (targetMap.find(name) == targetMap.end()) {
            piece.name = name;
            piece.detectionCount = 1;
            targetMap[name] = piece;
        } else {
            auto& existing = targetMap[name];
            existing.detectionCount++;
            
            if (existing.detectionCount >= lockThresholdCount && !existing.locked) {
                existing.locked = true;
                existing.motorCoords = convertToMotorCoords(
                    existing.position.x, existing.position.y
                );
                
                emit detectionInfo(QString("%1 坐标已锁定: 图像(%2,%3), 电机(%4,%5)")
                                  .arg(QString::fromStdString(name))
                                  .arg(existing.position.x).arg(existing.position.y)
                                  .arg(existing.motorCoords.x).arg(existing.motorCoords.y));
                
                emit pieceLocked(name, existing.motorCoords.x, existing.motorCoords.y);
            }
        }
    }
    
    for (const auto& [name, piece] : finalWhitePieces) {
        if (piece.locked) {
            cv::circle(displayFrame, piece.position, 15, cv::Scalar(0, 0, 255), 2);
            cv::putText(displayFrame, name, piece.position - cv::Point(10, 10),
                       cv::FONT_HERSHEY_SIMPLEX, 0.8, cv::Scalar(0, 0, 255), 2);
        }
    }
    
    for (const auto& [name, piece] : finalBlackPieces) {
        if (piece.locked) {
            cv::circle(displayFrame, piece.position, 15, cv::Scalar(255, 0, 0), 2);
            cv::putText(displayFrame, name, piece.position - cv::Point(10, 10),
                       cv::FONT_HERSHEY_SIMPLEX, 0.8, cv::Scalar(255, 0, 0), 2);
        }
    }
    
    cv::cvtColor(displayFrame, displayFrame, cv::COLOR_BGR2RGB);
    QImage img(displayFrame.data, displayFrame.cols, displayFrame.rows, 
              displayFrame.step, QImage::Format_RGB888);
    emit imageProcessed(img.copy());
    
    packAndPrintData();
    
    // 验证逻辑
    if (m_verificationPending) {
        bool found = false;
        QString detectedColor = "";
        
        for (const auto& piece : boardPieces) {
            if (piece.boardCoord.x == m_verifyBoardX && 
                piece.boardCoord.y == m_verifyBoardY) {
                
                found = true;
                detectedColor = QString::fromStdString(piece.color);
                break;
            }
        }
        
        if (found) {
            // 根据玩家角色和预期角色确定预期颜色
            QString expectedColor = "";
            if (m_expectedRole == PLAYER) {
                // 玩家落子
                expectedColor = (m_playerRole == PLAYER) ? "黑" : "白";
            } else {
                // 电脑落子
                expectedColor = (m_playerRole == PLAYER) ? "白" : "黑";
            }
            
            bool success = (detectedColor == expectedColor);
            
            emit verificationResult(m_verifyBoardX, m_verifyBoardY, success);
            
            QString result = success ? "成功" : "失败";
            QString info = QString("验证: (%1,%2) 检测:%3 预期:%4 - %5")
                        .arg(m_verifyBoardX).arg(m_verifyBoardY)
                        .arg(detectedColor)
                        .arg(expectedColor)
                        .arg(result);
            emit detectionInfo(info);
            
            if (success) {
                m_verificationPending = false;
                m_verificationTimer->stop();
            }
        } else {
            // 没有找到棋子，验证失败
            emit verificationResult(m_verifyBoardX, m_verifyBoardY, false);
            emit detectionInfo(QString("验证: (%1,%2) 未检测到棋子 - 失败")
                            .arg(m_verifyBoardX).arg(m_verifyBoardY));
        }
    }
    
}

void ChessDetector::packAndPrintData() {
    QString info;
    
    for (const auto& piece : boardPieces) {
        info += QString("棋盘格 %1: 位置(%2,%3), 状态: %4\n")
                .arg(QString::fromStdString(piece.name))
                .arg(piece.position.x).arg(piece.position.y)
                .arg(QString::fromStdString(piece.color));
    }
    
    for (const auto& [name, piece] : finalWhitePieces) {
        if (piece.locked) {
            info += QString("白子 %1: 图像位置(%2,%3), 电机位置(%4,%5)\n")
                    .arg(QString::fromStdString(name))
                    .arg(piece.position.x).arg(piece.position.y)
                    .arg(piece.motorCoords.x).arg(piece.motorCoords.y);
        }
    }
    
    for (const auto& [name, piece] : finalBlackPieces) {
        if (piece.locked) {
            info += QString("黑子 %1: 图像位置(%2,%3), 电机位置(%4,%5)\n")
                    .arg(QString::fromStdString(name))
                    .arg(piece.position.x).arg(piece.position.y)
                    .arg(piece.motorCoords.x).arg(piece.motorCoords.y);
        }
    }
    
    emit detectionInfo(info);
}

void ChessDetector::processVideo() {
    if (!cap.isOpened()) return;
    
    cv::Mat frame;
    cap >> frame;
    
    if (!frame.empty()) {
        processFrame(frame);
    }
}

void ChessDetector::startDetection() {
    if (cap.isOpened()) {
        timer->start(30);
    }
}

void ChessDetector::stopDetection() {
    timer->stop();
}

void ChessDetector::startContinuousVerification(int boardX, int boardY, ROLE expectedRole)
{
    m_verifyBoardX = boardX;
    m_verifyBoardY = boardY;
    m_expectedRole = expectedRole;
    m_verificationPending = true;
    
    m_verificationTimer->start();
    
    if (!timer->isActive()) {
        timer->start(30);
    }
}
